/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
 
This file is part of Quake III Arena source code.
 
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
 
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
//
//
// gameinfo.c
//

#include "ui_local.h"


//
// arena and bot info
//

#define POOLSIZE	128 * 1024

int				ui_numBots;
static char		*ui_botInfos[MAX_BOTS];

static int		ui_numArenas;
static char		*ui_arenaInfos[MAX_ARENAS];

static int		ui_numSinglePlayerArenas;
static int		ui_numSpecialSinglePlayerArenas;

static int		outOfMemory;

/*
===============
UI_ParseInfos
===============
*/
int UI_ParseInfos( char *buf, int max, char *infos[] )
{
    char	*token;
    int		count;
    char	key[MAX_TOKEN_CHARS];
    char	info[MAX_INFO_STRING];

    count = 0;

    while ( 1 )
    {
        token = COM_Parse( &buf );
        if ( !token[0] )
        {
            break;
        }
        if ( strcmp( token, "{" ) )
        {
            Com_Printf( "Missing { in info file\n" );
            break;
        }

        if ( count == max )
        {
            Com_Printf( "Max infos exceeded\n" );
            break;
        }

        info[0] = '\0';
        while ( 1 )
        {
            token = COM_ParseExt( &buf, qtrue );
            if ( !token[0] )
            {
                Com_Printf( "Unexpected end of info file\n" );
                break;
            }
            if ( !strcmp( token, "}" ) )
            {
                break;
            }
            Q_strncpyz( key, token, sizeof( key ) );

            token = COM_ParseExt( &buf, qfalse );
            if ( !token[0] )
            {
                strcpy( token, "<NULL>" );
            }
            Info_SetValueForKey( info, key, token );
        }
        //NOTE: extra space for arena number
        infos[count] = (char *)UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
        if (infos[count])
        {
            strcpy(infos[count], info);
            count++;
        }
    }
    return count;
}

/*
===============
UI_LoadArenasFromFile
===============
*/
static void UI_LoadArenasFromFile( char *filename )
{
    int				len;
    fileHandle_t	f;
    char			buf[MAX_ARENAS_TEXT];

    len = FS_FOpenFileByMode( filename, &f, FS_READ );
    if ( !f )
    {
        Com_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
        return;
    }
    if ( len >= MAX_ARENAS_TEXT )
    {
        Com_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) );
        FS_FCloseFile( f );
        return;
    }

    FS_Read2( buf, len, f );
    buf[len] = 0;
    FS_FCloseFile( f );

    ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] );
}

/*
===============
UI_LoadArenas
===============
*/
static void UI_LoadArenas( void )
{
    int			numdirs;
    cvar_t	*arenasFile;
    char		filename[128];
    char		dirlist[1024];
    char*		dirptr;
    int			i, n;
    int			dirlen;
    char		*type;
    char		*tag;
    int			singlePlayerNum, specialNum, otherNum;

    ui_numArenas = 0;

    arenasFile = Cvar_Get("g_arenasFile", "", CVAR_INIT|CVAR_ROM );
    if( *arenasFile->string )
    {
        UI_LoadArenasFromFile(arenasFile->string);
    }
    else
    {
        UI_LoadArenasFromFile("scripts/arenas.txt");
    }

    // get all arenas from .arena files
    numdirs = FS_GetFileList("scripts", ".arena", dirlist, 1024 );
    dirptr  = dirlist;
    for (i = 0; i < numdirs; i++, dirptr += dirlen+1)
    {
        dirlen = strlen(dirptr);
        strcpy(filename, "scripts/");
        strcat(filename, dirptr);
        UI_LoadArenasFromFile(filename);
    }
    Com_Printf( va( "%i arenas parsed\n", ui_numArenas ) );
    if (outOfMemory) Com_Printf(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n");

    // set initial numbers
    for( n = 0; n < ui_numArenas; n++ )
    {
        Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", n ) );
    }

    // go through and count single players levels
    ui_numSinglePlayerArenas = 0;
    ui_numSpecialSinglePlayerArenas = 0;
    for( n = 0; n < ui_numArenas; n++ )
    {
        // determine type
        type = Info_ValueForKey( ui_arenaInfos[n], "type" );

        // if no type specified, it will be treated as "ffa"
        if( !*type )
        {
            continue;
        }

        if( strstr( type, "single" ) )
        {
            // check for special single player arenas (training, final)
            tag = Info_ValueForKey( ui_arenaInfos[n], "special" );
            if( *tag )
            {
                ui_numSpecialSinglePlayerArenas++;
                continue;
            }

            ui_numSinglePlayerArenas++;
        }
    }

    n = ui_numSinglePlayerArenas % ARENAS_PER_TIER;
    if( n != 0 )
    {
        ui_numSinglePlayerArenas -= n;
        Com_Printf( va( "%i arenas ignored to make count divisible by %i\n", n, ARENAS_PER_TIER ) );
    }

    // go through once more and assign number to the levels
    singlePlayerNum = 0;
    specialNum = singlePlayerNum + ui_numSinglePlayerArenas;
    otherNum = specialNum + ui_numSpecialSinglePlayerArenas;
    for( n = 0; n < ui_numArenas; n++ )
    {
        // determine type
        type = Info_ValueForKey( ui_arenaInfos[n], "type" );

        // if no type specified, it will be treated as "ffa"
        if( *type )
        {
            if( strstr( type, "single" ) )
            {
                // check for special single player arenas (training, final)
                tag = Info_ValueForKey( ui_arenaInfos[n], "special" );
                if( *tag )
                {
                    Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", specialNum++ ) );
                    continue;
                }

                Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", singlePlayerNum++ ) );
                continue;
            }
        }

        Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", otherNum++ ) );
    }
}

/*
===============
UI_GetArenaInfoByNumber
===============
*/
const char *UI_GetArenaInfoByNumber( int num )
{
    int		n;
    char	*value;

    if( num < 0 || num >= ui_numArenas )
    {
        Com_Printf( va( S_COLOR_RED "Invalid arena number: %i\n", num ) );
        return NULL;
    }

    for( n = 0; n < ui_numArenas; n++ )
    {
        value = Info_ValueForKey( ui_arenaInfos[n], "num" );
        if( *value && atoi(value) == num )
        {
            return ui_arenaInfos[n];
        }
    }

    return NULL;
}


/*
===============
UI_GetArenaInfoByNumber
===============
*/
const char *UI_GetArenaInfoByMap( const char *map )
{
    int			n;

    for( n = 0; n < ui_numArenas; n++ )
    {
        if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "map" ), map ) == 0 )
        {
            return ui_arenaInfos[n];
        }
    }

    return NULL;
}


/*
===============
UI_GetSpecialArenaInfo
===============
*/
const char *UI_GetSpecialArenaInfo( const char *tag )
{
    int			n;

    for( n = 0; n < ui_numArenas; n++ )
    {
        if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "special" ), tag ) == 0 )
        {
            return ui_arenaInfos[n];
        }
    }

    return NULL;
}

/*
===============
UI_LoadBotsFromFile
===============
*/
static void UI_LoadBotsFromFile( char *filename )
{
    int				len;
    fileHandle_t	f;
    char			buf[MAX_BOTS_TEXT];

    len = FS_FOpenFileByMode( filename, &f, FS_READ );
    if ( !f )
    {
        Com_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
        return;
    }
    if ( len >= MAX_BOTS_TEXT )
    {
        Com_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
        FS_FCloseFile( f );
        return;
    }

    FS_Read2( buf, len, f );
    buf[len] = 0;
    FS_FCloseFile( f );

    ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] );
    if (outOfMemory) Com_Printf(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all bots\n");
}

/*
===============
UI_LoadBots
===============
*/
static void UI_LoadBots( void )
{
    cvar_t	*botsFile;
    int			numdirs;
    char		filename[128];
    char		dirlist[1024];
    char*		dirptr;
    int			i;
    int			dirlen;

    ui_numBots = 0;

    botsFile = Cvar_Get("g_botsFile", "", CVAR_INIT|CVAR_ROM );
    if( *botsFile->string )
    {
        UI_LoadBotsFromFile(botsFile->string);
    }
    else
    {
        UI_LoadBotsFromFile("scripts/bots.txt");
    }

    // get all bots from .bot files
    numdirs = FS_GetFileList("scripts", ".bot", dirlist, 1024 );
    dirptr  = dirlist;
    for (i = 0; i < numdirs; i++, dirptr += dirlen+1)
    {
        dirlen = strlen(dirptr);
        strcpy(filename, "scripts/");
        strcat(filename, dirptr);
        UI_LoadBotsFromFile(filename);
    }
    Com_Printf( va( "%i bots parsed\n", ui_numBots ) );
}


/*
===============
UI_GetBotInfoByNumber
===============
*/
char *UI_GetBotInfoByNumber( int num )
{
    if( num < 0 || num >= ui_numBots )
    {
        Com_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
        return NULL;
    }
    return ui_botInfos[num];
}


/*
===============
UI_GetBotInfoByName
===============
*/
char *UI_GetBotInfoByName( const char *name )
{
    int		n;
    char	*value;

    for ( n = 0; n < ui_numBots ; n++ )
    {
        value = Info_ValueForKey( ui_botInfos[n], "name" );
        if ( !Q_stricmp( value, name ) )
        {
            return ui_botInfos[n];
        }
    }

    return NULL;
}


//
// single player game info
//

/*
===============
UI_GetBestScore
 
Returns the player's best finish on a given level, 0 if the have not played the level
===============
*/
void UI_GetBestScore( int level, int *score, int *skill )
{
    int		n;
    int		skillScore;
    int		bestScore;
    int		bestScoreSkill;
    char	arenaKey[16];
    char	scores[MAX_INFO_VALUE];

    if( !score || !skill )
    {
        return;
    }

    if( level < 0 || level > ui_numArenas )
    {
        return;
    }

    bestScore = 0;
    bestScoreSkill = 0;

    for( n = 1; n <= 5; n++ )
    {
        Cvar_VariableStringBuffer( va( "g_spScores%i", n ), scores, MAX_INFO_VALUE );

        Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
        skillScore = atoi( Info_ValueForKey( scores, arenaKey ) );

        if( skillScore < 1 || skillScore > 8 )
        {
            continue;
        }

        if( !bestScore || skillScore <= bestScore )
        {
            bestScore = skillScore;
            bestScoreSkill = n;
        }
    }

    *score = bestScore;
    *skill = bestScoreSkill;
}


/*
===============
UI_SetBestScore
 
Set the player's best finish for a level
===============
*/
void UI_SetBestScore( int level, int score )
{
    int		skill;
    int		oldScore;
    char	arenaKey[16];
    char	scores[MAX_INFO_VALUE];

    // validate score
    if( score < 1 || score > 8 )
    {
        return;
    }

    // validate skill
    skill = (int)Cvar_VariableValue( "g_spSkill" );
    if( skill < 1 || skill > 5 )
    {
        return;
    }

    // get scores
    Cvar_VariableStringBuffer( va( "g_spScores%i", skill ), scores, MAX_INFO_VALUE );

    // see if this is better
    Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
    oldScore = atoi( Info_ValueForKey( scores, arenaKey ) );
    if( oldScore && oldScore <= score )
    {
        return;
    }

    // update scores
    Info_SetValueForKey( scores, arenaKey, va( "%i", score ) );
    Cvar_Set( va( "g_spScores%i", skill ), scores );
}


/*
===============
UI_LogAwardData
===============
*/
void UI_LogAwardData( int award, int data )
{
    char	key[16];
    char	awardData[MAX_INFO_VALUE];
    int		oldValue;

    if( data == 0 )
    {
        return;
    }

    if( award > AWARD_PERFECT )
    {
        Com_Printf( va( S_COLOR_RED "Bad award %i in UI_LogAwardData\n", award ) );
        return;
    }

    Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) );

    Com_sprintf( key, sizeof(key), "a%i", award );
    oldValue = atoi( Info_ValueForKey( awardData, key ) );

    Info_SetValueForKey( awardData, key, va( "%i", oldValue + data ) );
    Cvar_Set( "g_spAwards", awardData );
}


/*
===============
UI_GetAwardLevel
===============
*/
int UI_GetAwardLevel( int award )
{
    char	key[16];
    char	awardData[MAX_INFO_VALUE];

    Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) );

    Com_sprintf( key, sizeof(key), "a%i", award );
    return atoi( Info_ValueForKey( awardData, key ) );
}


/*
===============
UI_TierCompleted
===============
*/
int UI_TierCompleted( int levelWon )
{
    int			level;
    int			n;
    int			tier;
    int			score;
    int			skill;
    const char	*info;

    tier = levelWon / ARENAS_PER_TIER;
    level = tier * ARENAS_PER_TIER;

    if( tier == UI_GetNumSPTiers() )
    {
        info = UI_GetSpecialArenaInfo( "training" );
        if( levelWon == atoi( Info_ValueForKey( info, "num" ) ) )
        {
            return 0;
        }
        info = UI_GetSpecialArenaInfo( "final" );
        if( !info || levelWon == atoi( Info_ValueForKey( info, "num" ) ) )
        {
            return tier + 1;
        }
        return -1;
    }

    for( n = 0; n < ARENAS_PER_TIER; n++, level++ )
    {
        UI_GetBestScore( level, &score, &skill );
        if ( score != 1 )
        {
            return -1;
        }
    }
    return tier + 1;
}


/*
===============
UI_ShowTierVideo
===============
*/
qboolean UI_ShowTierVideo( int tier )
{
    char	key[16];
    char	videos[MAX_INFO_VALUE];

    if( tier <= 0 )
    {
        return qfalse;
    }

    Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) );

    Com_sprintf( key, sizeof(key), "tier%i", tier );
    if( atoi( Info_ValueForKey( videos, key ) ) )
    {
        return qfalse;
    }

    Info_SetValueForKey( videos, key, va( "%i", 1 ) );
    Cvar_Set( "g_spVideos", videos );

    return qtrue;
}


/*
===============
UI_CanShowTierVideo
===============
*/
qboolean UI_CanShowTierVideo( int tier )
{
    char	key[16];
    char	videos[MAX_INFO_VALUE];

    if( !tier )
    {
        return qfalse;
    }

    if( uis.demoversion && tier != 8 )
    {
        return qfalse;
    }

    Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) );

    Com_sprintf( key, sizeof(key), "tier%i", tier );
    if( atoi( Info_ValueForKey( videos, key ) ) )
    {
        return qtrue;
    }

    return qfalse;
}


/*
===============
UI_GetCurrentGame
 
Returns the next level the player has not won
===============
*/
int UI_GetCurrentGame( void )
{
    int		level;
    int		rank;
    int		skill;
    const char *info;

    info = UI_GetSpecialArenaInfo( "training" );
    if( info )
    {
        level = atoi( Info_ValueForKey( info, "num" ) );
        UI_GetBestScore( level, &rank, &skill );
        if ( !rank || rank > 1 )
        {
            return level;
        }
    }

    for( level = 0; level < ui_numSinglePlayerArenas; level++ )
    {
        UI_GetBestScore( level, &rank, &skill );
        if ( !rank || rank > 1 )
        {
            return level;
        }
    }

    info = UI_GetSpecialArenaInfo( "final" );
    if( !info )
    {
        return -1;
    }
    return atoi( Info_ValueForKey( info, "num" ) );
}


/*
===============
UI_NewGame
 
Clears the scores and sets the difficutly level
===============
*/
void UI_NewGame( void )
{
    Cvar_Set( "g_spScores1", "" );
    Cvar_Set( "g_spScores2", "" );
    Cvar_Set( "g_spScores3", "" );
    Cvar_Set( "g_spScores4", "" );
    Cvar_Set( "g_spScores5", "" );
    Cvar_Set( "g_spAwards", "" );
    Cvar_Set( "g_spVideos", "" );
}


/*
===============
UI_GetNumArenas
===============
*/
int UI_GetNumArenas( void )
{
    return ui_numArenas;
}


/*
===============
UI_GetNumSPArenas
===============
*/
int UI_GetNumSPArenas( void )
{
    return ui_numSinglePlayerArenas;
}


/*
===============
UI_GetNumSPTiers
===============
*/
int UI_GetNumSPTiers( void )
{
    return ui_numSinglePlayerArenas / ARENAS_PER_TIER;
}


/*
===============
UI_GetNumBots
===============
*/
int UI_GetNumBots( void )
{
    return ui_numBots;
}


/*
===============
UI_SPUnlock_f
===============
*/
void UI_SPUnlock_f( void )
{
    char	arenaKey[16];
    char	scores[MAX_INFO_VALUE];
    int		level;
    int		tier;

    // get scores for skill 1
    Cvar_VariableStringBuffer( "g_spScores1", scores, MAX_INFO_VALUE );

    // update scores
    for( level = 0; level < ui_numSinglePlayerArenas + ui_numSpecialSinglePlayerArenas; level++ )
    {
        Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
        Info_SetValueForKey( scores, arenaKey, "1" );
    }
    Cvar_Set( "g_spScores1", scores );

    // unlock cinematics
    for( tier = 1; tier <= 8; tier++ )
    {
        UI_ShowTierVideo( tier );
    }

    Com_Printf( "All levels unlocked at skill level 1\n" );

    UI_SPLevelMenu_ReInit();
}


/*
===============
UI_SPUnlockMedals_f
===============
*/
void UI_SPUnlockMedals_f( void )
{
    int		n;
    char	key[16];
    char	awardData[MAX_INFO_VALUE];

    Cvar_VariableStringBuffer( "g_spAwards", awardData, MAX_INFO_VALUE );

    for( n = 0; n < 6; n++ )
    {
        Com_sprintf( key, sizeof(key), "a%i", n );
        Info_SetValueForKey( awardData, key, "100" );
    }

    Cvar_Set( "g_spAwards", awardData );

    Com_Printf( "All levels unlocked at 100\n" );
}


/*
===============
UI_InitGameinfo
===============
*/
void UI_InitGameinfo( void )
{

    UI_InitMemory();
    UI_LoadArenas();
    UI_LoadBots();

    if( (Cvar_VariableValue( "fs_restrict" )) || (ui_numSpecialSinglePlayerArenas == 0 && ui_numSinglePlayerArenas == 4) )
    {
        uis.demoversion = qtrue;
    }
    else
    {
        uis.demoversion = qfalse;
    }
}
